<template>
  <div class="pro-upload-container">
    <n-upload
      ref="uploadRef"
      abstract
      :accept="props.accept"
      :multiple="props.multiple"
      :directory="props.directory"
      :default-upload="props.defaultUpload"
      :show-file-list="false"
      :on-before-upload="handleBeforeUpload"
      :custom-request="handleCustomRequest"
      :on-change="onChange"
    >
      <div class="upload-container">
        <div class="upload-trigger">
          <slot name="trigger">
            <n-upload-trigger #="{ handleClick }" abstract>
              <n-button
                @click="handleClick"
                :disabled="props.disabled || props.max === fileList.length"
              >
                上传文件
              </n-button>
            </n-upload-trigger>
          </slot>
        </div>
        <div class="upload-tip" v-if="props.showTip">
          <slot name="tip">
            <n-alert type="warning" class="alert-warning">
              {{
                allowedFileType === '*'
                  ? '支持上传全部类型文件'
                  : `请上传 ${allowedFileType} 文件`
              }}
              {{
                props.maxFileSize
                  ? `，且文件大小不超过 ${transformSize(props.maxFileSize)}`
                  : ''
              }}
              {{ props.max ? `，文件数量不超过 ${props.max}个` : '' }}
            </n-alert>
          </slot>
        </div>
      </div>
    </n-upload>

    <div class="upload-list" v-if="props.showFileList">
      <pro-upload-list
        ref="proUploadListRef"
        v-bind="$attrs"
        :file-list="fileList"
        :show-cancel-button="operateProps.cancel"
        :show-download-button="operateProps.download"
        :show-delete-button="operateProps.delete"
        :show-retry-button="operateProps.retry"
        @retry="handleRetry"
        @cancel="handleCancel"
        @delete="handleDelete"
        @download="handleDownload"
      />
    </div>
  </div>
</template>

<script lang="ts" setup name="ProUpload">
  import { NUpload } from 'naive-ui'
  import { transformSize } from '@/utils/file/file'
  import ProUploadList from '../pro-upload-list/index.vue'
  import {
    FileInfo,
    UploadOptions,
    CustomRequestType,
    HttpPropsType,
    OperateType
  } from './type'
  import useUpload from './useUpload'

  interface UploadPropsType {
    accept?: string
    maxFileSize?: number
    max?: number
    multiple?: boolean
    showTip?: boolean
    disabled?: boolean
    directory?: boolean
    showFileList?: boolean
    http?: HttpPropsType
    operate?: OperateType
    defaultUpload?: boolean
    onFileTypeCorrect?: (file: File) => boolean
    onBeforeUpload?: (options: UploadOptions) => boolean | Promise<boolean>
    createThumbnailUrl?: (file: File) => Promise<string>
    customRequest?: (options: CustomRequestType) => Promise<any>
    getResponse?: (response: unknown) => unknown
    onFinish?: (file: FileInfo, response: unknown) => void
  }

  const defaultHttpProps: HttpPropsType = {
    method: 'post',
    headers: {},
    name: 'file'
  }

  const defaultOperateProps: OperateType = {
    cancel: true,
    delete: true,
    retry: true,
    download: true
  }

  const props = withDefaults(defineProps<UploadPropsType>(), {
    accept: '*',
    maxFileSize: 1024 * 1024 * 20,
    max: 100,
    multiple: true,
    showTip: true,
    showFileList: true,
    defaultUpload: true,
    http: () => ({}),
    operate: () => ({})
  })

  const httpProps = computed(() => {
    return { ...defaultHttpProps, ...props.http }
  })

  const operateProps = computed(() => {
    return { ...defaultOperateProps, ...props.operate }
  })

  const emits = defineEmits(['update', 'retry', 'cancel', 'download', 'delete'])

  const uploadRef = ref<typeof NUpload | null>(null)
  const {
    fileList,
    onChange,
    handleBeforeUpload,
    handleCustomRequest,
    handleRetry,
    handleDelete,
    handleCancel,
    handleDownload,
    handleClear
  } = useUpload(props, httpProps, uploadRef, emits)

  /**  用于提示的类型文本  **/
  const allowedFileType = computed(() => {
    if (props.accept.includes('*')) return '*'
    else
      return props.accept
        .split(',')
        .map((i) => i.replace('.', ''))
        .join(' 、')
  })

  // 获取上传响应结果数据
  const proUploadListRef = ref<typeof ProUploadList | null>(null)
  const getUploadResult = (): unknown[] => {
    return unref(proUploadListRef)?.getUploadResult()
  }

  // 判断是否上传完毕（列表中如果有正在上传或者有失败的文件，则返回false）
  const isAllDone = (): boolean => {
    return unref(proUploadListRef)?.isAllDone()
  }

  // 获取已成功上传文件列表
  const getSuccessFileList = (): {
    fileList: Array<FileInfo>
    length: number
  } => {
    return unref(proUploadListRef)?.getSuccessFileList()
  }
  // 获取全部上传文件列表
  const getAllFileList = (): { fileList: Array<FileInfo>; length: number } => {
    return unref(proUploadListRef)?.getAllFileList()
  }
  defineExpose({
    getAllFileList,
    getSuccessFileList,
    getUploadResult,
    isAllDone,
    handleRetry,
    handleDelete,
    handleCancel,
    handleDownload,
    submit: () => uploadRef.value?.submit(),
    clear: handleClear
  })
</script>

<style lang="less" scoped>
  .upload-container {
    display: flex;
    align-items: center;

    .upload-tip {
      margin-left: 15px;
    }

    :deep(.alert-warning) {
      .n-alert-body {
        padding-top: 5px;
        padding-bottom: 5px;
      }

      .n-alert__icon {
        margin-top: 4px;
      }
    }
  }
  .upload-list {
    width: 100%;
    margin-top: 20px;
  }
</style>
